const getClass = require('../getClass');

function Array2DRowRealMatrix(arg0, arg1) {
  this.data = null;

  this.copyIn = function (_in) {
    this.setSubMatrix(_in, 0, 0);
  }

  this.setSubMatrix = function (subMatrix, row, column) {
    if (this.data == null) {
      if (row > 0) {
        throw new Error(LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row);
      }
      if (column > 0) {
        throw new Error(LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column);
      }
      require('./MathUtils').checkNotNull(subMatrix);
      var nRows = subMatrix.length;
      if (nRows == 0) {
        throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
      }
      var nCols = subMatrix[0].length;
      if (nCols == 0) {
        throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
      }
      this.data = new Array(subMatrix.length);
      for (var i = 0; i < subMatrix.length; i++) {
        this.data[i] = new Array(nCols)
      }
      for (var i = 0; i < this.data.length; ++i) {
        if (subMatrix[i].length != nCols) {
          throw new DimensionMismatchException(subMatrix[i].length, nCols);
        }
        for (var j = 0; j < nCols; j++) {
          this.data[i + row][j + column] = subMatrix[i][j]
        }
      }
    } else {
      require('./MathUtils').checkNotNull(subMatrix);
      var nRows = subMatrix.length;
      if (nRows == 0) {
        throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
      }
      var nCols = subMatrix[0].length;
      if (nCols == 0) {
        throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
      }
      for (var r = 1; r < nRows; ++r) {
        if (subMatrix[r].length != nCols) {
          throw new DimensionMismatchException(nCols, subMatrix[r].length);
        }
      }
      MatrixUtils.checkRowIndex(this, row);
      MatrixUtils.checkColumnIndex(this, column);
      MatrixUtils.checkRowIndex(this, nRows + row - 1);
      MatrixUtils.checkColumnIndex(this, nCols + column - 1);
      for (var i = 0; i < nRows; ++i) {
        for (var j = 0; j < nCols; ++j) {
          setEntry(row + i, column + j, subMatrix[i][j]);
        }
      }
    }

  }

  this.isSquare = function () {
    return this.getColumnDimension() == this.getRowDimension();
  }

  this.getRowDimension = function () {
    return (this.data == null) ? 0 : this.data.length;
  }

  this.getColumnDimension = function () {
    return ((this.data == null) || (this.data[0] == null)) ? 0 : this.data[0].length;
  }

  this.getData = function () {
    return this.copyOut();
  }

  this.copyOut = function () {
    var nRows = this.getRowDimension();
    var nCols = this.getColumnDimension();
    var out = new Array(nRows);
    // can't copy 2-d array in one shot, otherwise get row references
    for (var i = 0; i < nRows; i++) {
      out[i] = new Array(nCols);
      for (var j = 0; j < this.data[i].length; j++) {
        out[i][j] = this.data[i][j]
      }
    }
    return out;
  }

  this.setEntry = function (row, column, value) {
    require('./MatrixUtils').checkMatrixIndex(this, row, column);
    this.data[row][column] = value;
  }

  this.getEntry = function (row, column) {
    require('./MatrixUtils').checkMatrixIndex(this, row, column);
    return this.data[row][column];
  }

  this.multiply = function (m) {
    require('./MatrixUtils').checkMultiplicationCompatible(this, m);
    var nRows = this.getRowDimension();
    var nCols = m.getColumnDimension();
    var nSum = this.getColumnDimension();
    var outData = new Array(nRows);
    for (var i = 0; i < nRows; i++) {
      outData[i] = new Array(nCols)
    }

    // Will hold a column of "m".
    var mCol = new Array(nSum);
    var mData = m.data;

    // Multiply.
    for (var col = 0; col < nCols; col++) {
      // Copy all elements of column "col" of "m" so that
      // will be in contiguous memory.
      for (var mRow = 0; mRow < nSum; mRow++) {
        mCol[mRow] = mData[mRow][col];
      }
      for (var row = 0; row < nRows; row++) {
        var dataRow = this.data[row];
        var sum = 0;
        for (var i = 0; i < nSum; i++) {
          sum += dataRow[i] * mCol[i];
        }
        outData[row][col] = sum;
      }
    }
    return new Array2DRowRealMatrix(outData, false);
  }

  if (getClass(arg0) == 'Array') {
    this.copyIn(arg0)
  } else if (getClass(arg0) == 'Number' && getClass(arg1) == 'Number') {
    if (arg0 < 1) {
      throw new NotStrictlyPositiveException(arg0);
    }
    if (arg1 < 1) {
      throw new NotStrictlyPositiveException(arg1);
    }
    this.data = new Array(arg0);
    for (var i = 0; i < arg0; i++) {
      this.data[i] = new Array(arg1);
      for (var j = 0; j < arg1; j++) {
        this.data[i][j] = 0
      }
    }
  }

}

module.exports = Array2DRowRealMatrix
